package com.lytfly.flowable.modules.flow.service.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import com.lytfly.flowable.config.flow.cmd.MyClaimTaskCmd;
import com.lytfly.flowable.domain.CommentTypeEnum;
import com.lytfly.flowable.domain.FlowConstant;
import com.lytfly.flowable.domain.ProcessStatusEnum;
import com.lytfly.flowable.domain.Query;
import com.lytfly.flowable.domain.task.*;
import com.lytfly.flowable.entity.Personal;
import com.lytfly.flowable.exception.FlowException;
import com.lytfly.flowable.modules.flow.entity.FlowCommentInfo;
import com.lytfly.flowable.modules.flow.mapper.IFlowableTaskMapper;
import com.lytfly.flowable.modules.flow.service.BaseProcessService;
import com.lytfly.flowable.modules.flow.service.IBpmnModelService;
import com.lytfly.flowable.modules.flow.service.IFlowableTaskService;
import com.lytfly.flowable.service.IPersonalService;
import com.lytfly.flowable.utils.DurationUtils;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.engine.*;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.identitylink.api.IdentityLinkType;
import org.flowable.idm.api.User;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @program: flow
 * @description: 流程任务服务
 * @author: Bruce.Liu
 * @create: 2021-04-22 22:55
 **/
@Slf4j
@Service
public class FlowableTaskServiceImpl extends BaseProcessService implements IFlowableTaskService {

    @Autowired
    private IFlowableTaskMapper flowableTaskMapper;
    @Autowired
    private TaskService taskService;
    @Autowired
    private ManagementService managementService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private IPersonalService personalService;

    @Autowired
    private IBpmnModelService bpmnModelService;

    @Autowired
    private IdentityService identityService;


    /**
     * 通过任务id获取任务对象
     *
     * @param taskId 任务id
     * @return
     */
    @Override
    public Task findTaskById(String taskId) {
        return taskService.createTaskQuery().taskId(taskId).singleResult();
    }


    /**
     * 查询我的待办任务总数
     *
     * @param params
     * @return
     */
    @Override
    public Long getApplyingTaskCont(TaskQueryParamsVO params) {
        return flowableTaskMapper.getAppingTaskCont(params);
    }

    /**
     * 查询我的待办任务列表
     *
     * @param paramsvo 查询参数
     * @param query    分页
     * @return
     */
    @Override
    public List<TaskVO> getApplyingTaskListByPage(TaskQueryParamsVO paramsvo, Query query) {
        IPage<TaskVO> queryPage = new Page<>(query.getPageNum(), query.getPageSize());
        IPage<TaskVO> page = flowableTaskMapper.getAppingTasksPagerModel(queryPage, paramsvo);
        List<TaskVO> taskVOList = page.getRecords();
        this.setTaskVOData(taskVOList);
        return taskVOList;
    }

    /**
     * 审批
     *
     * @param completeTaskVO 执行任务VO
     * @throws FlowException
     */
    @Override
    public void complete(CompleteTaskVO completeTaskVO) throws FlowException {
        assert completeTaskVO != null;
        if (StrUtil.isNotBlank(completeTaskVO.getTaskId())) {
            //查询任务
            TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery()
                    .taskId(completeTaskVO.getTaskId())
                    .singleResult();
            assert taskEntity != null;
            if (StrUtil.isBlank(completeTaskVO.getProcessInstanceId())) {
                completeTaskVO.setProcessInstanceId(taskEntity.getProcessInstanceId());
            }
            this.evictHighLightedNodeCache(taskEntity.getProcessInstanceId());
            this.evictOneActivityVoCache(taskEntity.getProcessInstanceId(), taskEntity.getTaskDefinitionKey());
            String taskId = completeTaskVO.getTaskId();
            //任务正在协办
            if (DelegationState.PENDING.equals(taskEntity.getDelegationState())) {
                Task subTask = this.createSubTask(taskEntity, taskEntity.getParentTaskId(), completeTaskVO.getUserCode());
                taskService.complete(subTask.getId());
                taskId = subTask.getId();
                taskService.resolveTask(completeTaskVO.getTaskId(), completeTaskVO.getVariables());
            } else {
                //添加审批意见到流程的意见里面
                //taskService.addComment(taskId, taskEntity.getProcessInstanceId(), completeTaskVO.getCommentTypeEnum().name(), completeTaskVO.getMessage());
                //3.1修改执行人 其实我这里就相当于签收了
                //taskService.setAssignee(completeTaskVO.getTaskId(), completeTaskVO.getUserCode());
                flowableTaskMapper.updateHisAssignee(taskId, completeTaskVO.getUserCode());
                if (MapUtil.isNotEmpty(completeTaskVO.getVariables())) {
                    taskService.complete(completeTaskVO.getTaskId(), completeTaskVO.getVariables());
                } else {
                    taskService.complete(completeTaskVO.getTaskId());
                }
                //4.处理加签父任务
                String parentTaskId = taskEntity.getParentTaskId();
                if (StrUtil.isNotBlank(parentTaskId)) {
                    String tableName = managementService.getTableName(TaskEntity.class);
                    String sql = "select count(1) from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}";
                    long subTaskCount = taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count();
                    if (subTaskCount == 0) {
                        Task parentTask = taskService.createTaskQuery()
                                .taskId(parentTaskId)
                                .singleResult();
                        taskService.resolveTask(parentTaskId);
                        if (parentTask != null && CommentTypeEnum.HJQ.toString().equals(parentTask.getScopeType())) {
                            taskService.complete(parentTaskId);
                        }
                    }
                }
            }
            completeTaskVO.setTaskId(taskId);
            completeTaskVO.setActivityId(taskEntity.getTaskDefinitionKey());
            completeTaskVO.setActivityName(taskEntity.getName());
            this.addFlowCommentInfoAndProcessStatus(completeTaskVO);
            log.info("流程ID:{},任务ID:{},已审批完成!", taskEntity.getProcessInstanceId(), taskEntity.getId());
        } else {
            throw new FlowException("taskId should not be null!");
        }

    }


    /**
     * 查询我的已办任务
     *
     * @param paramsVo 查询参数
     * @param query    分页
     * @return
     */
    @Override
    public List<TaskVO> getAppliedTaskListByPage(TaskQueryParamsVO paramsVo, Query query) {
        IPage<TaskVO> queryPage = new Page<>(query.getPageNum(), query.getPageSize());
        IPage<TaskVO> page = flowableTaskMapper.getApplyedTasksPagerModel(queryPage, paramsVo);
        List<TaskVO> taskVOList = page.getRecords();
        this.setTaskVOData(taskVOList);
        return taskVOList;
    }

    /**
     * 完善任务的详细数据,时长、流程实例状态
     *
     * @param TaskVOList
     */
    private void setTaskVOData(List<TaskVO> TaskVOList) {
        if (CollUtil.isNotEmpty(TaskVOList)) {
            long currTime = System.currentTimeMillis();
            TaskVOList.forEach(taskVO -> {
                taskVO.setProcessStatusName(ProcessStatusEnum.getEnumMsgByType(taskVO.getProcessStatus()));
                long time = taskVO.getCreateTime().getTime();
                long duration = currTime - time;
                taskVO.setStayHour(DurationUtils.getDuration(duration));
                Date startTime = taskVO.getStartTime();
                Date finishedTime = taskVO.getFinishedTime();
                if (taskVO.getFinishedTime() == null) {
                    finishedTime = new Date();
                }
                long totalDuration = finishedTime.getTime() - startTime.getTime();
                taskVO.setTotalTime(DurationUtils.getDuration(totalDuration));
            });
        }
    }

    /**
     * 获取可驳回节点列表
     * 驳回：当前处理人可以驳回历史走过的任何一个节点
     * 1、驳回任意普通节点
     * 2、驳回多实例节点
     * 3、驳回并行网关节点
     * 4、驳回子流程节点
     * 5、子流程节点驳回主流程节点
     * <p>
     * 实际情况中，为了获取可驳回的节点列表，我们做了一些规定，比方说并行网关节点，要求必须成对出现，也只能驳回到并行网关节点的 fork节点
     * https://blog.csdn.net/liuwenjun05101/article/details/103482334?spm=1001.2014.3001.5501
     *
     * @param taskId            任务id
     * @param processInstanceId 流程实例id
     * @return
     */
    @Override
    public List<FlowNodeVO> getBackNodeListByProcessInstanceId(String processInstanceId, String taskId) {
        List<FlowNodeVO> backNods = new ArrayList<>();
        TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
        String currActId = taskEntity.getTaskDefinitionKey();
        //获取运行节点表中usertask
        String sql = "select t.* from act_ru_actinst t where t.ACT_TYPE_ = 'userTask' and t.PROC_INST_ID_=#{processInstanceId} and t.END_TIME_ is not null ";
        List<ActivityInstance> activityInstances = runtimeService.createNativeActivityInstanceQuery().sql(sql)
                .parameter("processInstanceId", processInstanceId)
                .list();
        //获取运行节点表的parallelGateway节点并出重
        sql = "SELECT t.ID_, t.REV_,t.PROC_DEF_ID_,t.PROC_INST_ID_,t.EXECUTION_ID_,t.ACT_ID_, t.TASK_ID_, t.CALL_PROC_INST_ID_, t.ACT_NAME_, t.ACT_TYPE_, " +
                " t.ASSIGNEE_, t.START_TIME_, max(t.END_TIME_) as END_TIME_, t.DURATION_, t.DELETE_REASON_, t.TENANT_ID_" +
                " FROM  act_ru_actinst t WHERE t.ACT_TYPE_ = 'parallelGateway' AND t.PROC_INST_ID_ = #{processInstanceId} and t.END_TIME_ is not null" +
                " and t.ACT_ID_ <> #{actId} GROUP BY t.act_id_";
        List<ActivityInstance> parallelGatewaies = runtimeService.createNativeActivityInstanceQuery().sql(sql)
                .parameter("processInstanceId", processInstanceId)
                .parameter("actId", currActId)
                .list();
        //排序
        if (CollUtil.isNotEmpty(parallelGatewaies)) {
            activityInstances.addAll(parallelGatewaies);
            activityInstances.sort(Comparator.comparing(ActivityInstance::getEndTime));
        }
        //分组节点
        int count = 0;
        Map<ActivityInstance, List<ActivityInstance>> parallelGatewayUserTasks = new HashMap<>();
        List<ActivityInstance> userTasks = new ArrayList<>();
        ActivityInstance currActivityInstance = null;
        for (ActivityInstance activityInstance : activityInstances) {
            if (BpmnXMLConstants.ELEMENT_GATEWAY_PARALLEL.equals(activityInstance.getActivityType())) {
                count++;
                if (count % 2 != 0) {
                    List<ActivityInstance> datas = new ArrayList<>();
                    currActivityInstance = activityInstance;
                    parallelGatewayUserTasks.put(currActivityInstance, datas);
                }
            }
            if (BpmnXMLConstants.ELEMENT_TASK_USER.equals(activityInstance.getActivityType())) {
                if (count % 2 == 0) {
                    userTasks.add(activityInstance);
                } else {
                    if (parallelGatewayUserTasks.containsKey(currActivityInstance)) {
                        parallelGatewayUserTasks.get(currActivityInstance).add(activityInstance);
                    }
                }
            }
        }
        //组装人员名称
        List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId).finished().list();
        Map<String, List<HistoricTaskInstance>> taskInstanceMap = new HashMap<>();
        List<String> userCodes = new ArrayList<>();
        historicTaskInstances.forEach(historicTaskInstance -> {
            userCodes.add(historicTaskInstance.getAssignee());
            String taskDefinitionKey = historicTaskInstance.getTaskDefinitionKey();
            if (taskInstanceMap.containsKey(historicTaskInstance.getTaskDefinitionKey())) {
                taskInstanceMap.get(taskDefinitionKey).add(historicTaskInstance);
            } else {
                List<HistoricTaskInstance> tasks = new ArrayList<>();
                tasks.add(historicTaskInstance);
                taskInstanceMap.put(taskDefinitionKey, tasks);
            }
        });
        //组装userTask的数据
        List<Personal> personalsByCodeList = personalService.getPersonalsByCodeList(userCodes);
        Map<String, String> activityIdUserNames = this.getApproverData(processInstanceId, personalsByCodeList, taskInstanceMap);
        if (CollUtil.isNotEmpty(userTasks)) {
            userTasks.forEach(activityInstance -> {
                FlowNodeVO node = new FlowNodeVO();
                node.setNodeId(activityInstance.getActivityId());
                node.setNodeName(activityInstance.getActivityName());
                node.setEndTime(activityInstance.getEndTime());
                node.setUserName(activityIdUserNames.get(activityInstance.getActivityId()));
                backNods.add(node);
            });
        }
        //组装会签节点数据
        if (MapUtil.isNotEmpty(taskInstanceMap)) {
            parallelGatewayUserTasks.forEach((activity, activities) -> {
                FlowNodeVO node = new FlowNodeVO();
                node.setNodeId(activity.getActivityId());
                node.setEndTime(activity.getEndTime());
                StringBuffer nodeNames = new StringBuffer("会签:");
                StringBuffer userNames = new StringBuffer("审批人员:");
                if (CollUtil.isNotEmpty(activities)) {
                    activities.forEach(activityInstance -> {
                        nodeNames.append(activityInstance.getActivityName()).append(",");
                        userNames.append(activityIdUserNames.get(activityInstance.getActivityId())).append(",");
                    });
                    node.setNodeName(nodeNames.toString());
                    node.setUserName(userNames.toString());
                    backNods.add(node);
                }
            });
        }
        //去重合并
        List<FlowNodeVO> dataList = backNods.stream().collect(
                Collectors.collectingAndThen(Collectors.toCollection(() ->
                        new TreeSet<>(Comparator.comparing(FlowNodeVO::getNodeId))), ArrayList::new));

        //排序
        dataList.sort(Comparator.comparing(FlowNodeVO::getEndTime));
        return dataList;
    }

    /**
     * 获取审批人详细信息
     *
     * @param processInstanceId
     * @param userList
     * @param taskInstanceMap
     * @return
     */
    private Map<String, String> getApproverData(String processInstanceId, List<Personal> userList, Map<String, List<HistoricTaskInstance>> taskInstanceMap) {
        Map<String, Personal> userMap = userList.stream().collect(Collectors.toMap(Personal::getId, personal -> personal));
        Map<String, String> applyMap = new HashMap<>(6);
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        taskInstanceMap.forEach((activityId, taskInstances) -> {
            StringJoiner finalApproverData = new StringJoiner(",");
            taskInstances.forEach(taskInstance -> {
                if (!taskInstance.getName().equals(FlowConstant.FLOW_SUBMITTER)) {
                    Personal personal = userMap.get(taskInstance.getAssignee());
                    if (personal != null) {
                        if (!StrUtil.contains(finalApproverData.toString(), personal.getName())) {
                            finalApproverData.add(personal.getName());
                        }
                    }
                } else {
                    String startUserId = processInstance.getStartUserId();
                    Personal personal = personalService.getPersonalByCode(startUserId);
                    if (personal != null) {
                        finalApproverData.add(personal.getName());
                    }
                }
            });
            applyMap.put(activityId, finalApproverData.toString());
        });
        return applyMap;
    }

    /**
     * 驳回任意节点 暂时没有考虑子流程
     *
     * @param backTaskVO 参数
     * @return
     */
    @Override
    public void backToStepTask(BackVO backTaskVO) {
        TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(backTaskVO.getTaskId()).singleResult();
        assert taskEntity != null;
        //1.把当前的节点设置为空
        //2.设置审批人
        taskEntity.setAssignee(backTaskVO.getUserCode());
        taskService.saveTask(taskEntity);
        //3.添加驳回意见
        FlowCommentInfo commentInfo = new FlowCommentInfo(CommentTypeEnum.BH.toString(), backTaskVO.getUserCode(),
                backTaskVO.getProcessInstanceId(),
                backTaskVO.getMessage());
        this.addFlowCommentInfo(commentInfo);
        //4.处理提交人节点
        FlowElement distActivity = bpmnModelService.getFlowElementByActivityIdAndProcessDefinitionId(backTaskVO.getDistFlowElementId(), taskEntity.getProcessDefinitionId());
        if (distActivity != null) {
            if (FlowConstant.FLOW_SUBMITTER.equals(distActivity.getName())) {
                ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(taskEntity.getProcessInstanceId()).singleResult();
                runtimeService.setVariable(backTaskVO.getProcessInstanceId(), FlowConstant.FLOW_SUBMITTER_VAR, processInstance.getStartUserId());
            }
        }
        //5.删除节点
        this.deleteActivity(backTaskVO.getDistFlowElementId(), taskEntity.getProcessInstanceId());
        List<String> executionIds = new ArrayList<>();
        //6.判断节点是不是子流程内部的节点
        if (bpmnModelService.isChildProcessNodeByActivityId(taskEntity.getProcessDefinitionId(),
                backTaskVO.getDistFlowElementId())
                && bpmnModelService.isChildProcessNodeByActivityId(taskEntity.getProcessDefinitionId(),
                taskEntity.getTaskDefinitionKey())) {
            //6.1 子流程内部驳回
            Execution executionTask = runtimeService.createExecutionQuery().executionId(taskEntity.getExecutionId()).singleResult();
            String parentId = executionTask.getParentId();
            List<Execution> executions = runtimeService.createExecutionQuery().parentId(parentId).list();
            executions.forEach(execution -> executionIds.add(execution.getId()));
            this.moveExecutionsToSingleActivityId(executionIds, backTaskVO.getDistFlowElementId());
        } else {
            //6.2 普通驳回
            List<Execution> executions = runtimeService.createExecutionQuery().parentId(taskEntity.getProcessInstanceId()).list();
            executions.forEach(execution -> executionIds.add(execution.getId()));
            this.moveExecutionsToSingleActivityId(executionIds, backTaskVO.getDistFlowElementId());
        }
        log.info("流程ID:{},任务ID:{},已驳回!", taskEntity.getProcessInstanceId(), taskEntity.getId());

    }

    /**
     * 任务前加签 （如果多次加签只能显示第一次前加签的处理人来处理任务）
     * 多个加签人处理完毕任务之后又流到自己这里
     * 向前加签:任务在A这里，A这个时候需要BCD核对一下，等BCD核对之后又回到A这里
     * https://blog.csdn.net/liuwenjun05101/article/details/103442244?spm=1001.2014.3001.5501
     *
     * @param addSignTaskVO 参数
     * @return
     */
    @Override
    public void beforeAddSignTask(SignVO addSignTaskVO) {
        this.addSignTask(addSignTaskVO, false);
    }

    /**
     * 任务后加签（加签人自己自动审批完毕加签多个人处理任务）
     * 向后加签:任务在A这里，A这个时候需要BCD处理这个事情，处理完毕之后就不用管了，继续后面的审批环节
     * https://blog.csdn.net/liuwenjun05101/article/details/103442244?spm=1001.2014.3001.5501
     *
     * @param addSignTaskVO 参数
     * @return
     */
    @Override
    public void afterAddSignTask(SignVO addSignTaskVO) {
        this.addSignTask(addSignTaskVO, true);
    }

    /**
     * 任务加签
     *
     * @param addSignTaskVo 参数
     * @param typeFlag      true:向后加签;false:向前加签;
     * @return
     */
    @Override
    public void addSignTask(SignVO addSignTaskVo, Boolean typeFlag) {
        TaskEntityImpl taskEntity = (TaskEntityImpl) taskService.createTaskQuery().taskId(addSignTaskVo.getTaskId()).singleResult();
        assert taskEntity != null;
        //1.把当前的节点设置为空
        //如果是加签再加签
        String parentTaskId = taskEntity.getParentTaskId();
        if (StrUtil.isBlank(parentTaskId)) {
            taskEntity.setOwner(addSignTaskVo.getUserCode());
            taskEntity.setAssignee(null);
            taskEntity.setCountEnabled(true);
            if (typeFlag) {
                taskEntity.setScopeType(CommentTypeEnum.HJQ.toString());
            } else {
                taskEntity.setScopeType(CommentTypeEnum.QJQ.toString());
            }
            //1.2 设置任务为空执行者
            taskService.saveTask(taskEntity);
        }
        //2.添加加签数据

        this.createSignSubTasks(addSignTaskVo, taskEntity);
        //3.添加审批意见
        String type = typeFlag ? CommentTypeEnum.HJQ.toString() : CommentTypeEnum.QJQ.toString();
        FlowCommentInfo commentInfo = new FlowCommentInfo(type, addSignTaskVo.getUserCode(),
                addSignTaskVo.getProcessInstanceId(),
                addSignTaskVo.getMessage());
        this.addFlowCommentInfo(commentInfo);
        log.info("流程ID:{},任务ID:{},已{}!", taskEntity.getProcessInstanceId(), taskEntity.getId(), type);
    }

    /**
     * 创建加签子任务
     *
     * @param signVo     加签参数
     * @param taskEntity 父任务
     */
    private void createSignSubTasks(SignVO signVo, TaskEntity taskEntity) {
        if (CollUtil.isNotEmpty(signVo.getSignPersoneds())) {
            String parentTaskId = taskEntity.getParentTaskId();
            if (StrUtil.isBlank(parentTaskId)) {
                parentTaskId = taskEntity.getId();
            }
            String finalParentTaskId = parentTaskId;
            //1.创建被加签人的任务列表
            signVo.getSignPersoneds().forEach(userCode -> {
                if (StrUtil.isNotBlank(userCode)) {
                    this.createSubTask(taskEntity, finalParentTaskId, userCode);
                }
            });
            String taskId = taskEntity.getId();
            if (StrUtil.isBlank(taskEntity.getParentTaskId())) {
                //2.创建加签人的任务并执行完毕
                Task task = this.createSubTask(taskEntity, finalParentTaskId, signVo.getUserCode());
                taskId = task.getId();
            }
            Task taskInfo = taskService.createTaskQuery().taskId(taskId).singleResult();
            if (null != taskInfo) {
                taskService.complete(taskId);
            }
            //如果是候选人，需要删除运行时候选表种的数据。
            long candidateCount = taskService.createTaskQuery().taskId(parentTaskId).taskCandidateUser(signVo.getUserCode()).count();
            if (candidateCount > 0) {
                taskService.deleteCandidateUser(parentTaskId, signVo.getUserCode());
            }
        }
    }

    /**
     * 反签收任务
     * 反签收：就是把执行人设置为空
     * 注意事项：反签收的时候，一定要先确定是否有候选人或者候选组，如果没有的话，不能反签收。因为会导致这个任务无法认领。
     * https://blog.csdn.net/liuwenjun05101/article/details/103438645?spm=1001.2014.3001.5501
     *
     * @param claimTaskVO 参数
     * @return
     */
    @Override
    public void unClaimTask(ClaimTaskVO claimTaskVO) {
        TaskEntityImpl currTask = (TaskEntityImpl) taskService.createTaskQuery().taskId(claimTaskVO.getTaskId()).singleResult();
        assert currTask != null;
        //1.添加审批意见
        FlowCommentInfo commentInfo = new FlowCommentInfo(CommentTypeEnum.FQS.toString(), claimTaskVO.getUserCode(),
                claimTaskVO.getProcessInstanceId(),
                claimTaskVO.getMessage());
        this.addFlowCommentInfo(commentInfo);
        List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(claimTaskVO.getTaskId());
        boolean flag = false;
        if (CollUtil.isNotEmpty(identityLinks)) {
            for (IdentityLink link : identityLinks) {
                if (IdentityLinkType.CANDIDATE.equals(link.getType())) {
                    flag = true;
                    break;
                }
            }
        }
        if (!flag) {
            throw new FlowableException("由于没有候选人或候选组,会导致任务无法认领,请确认.");
        }
        //2.反签收
        managementService.executeCommand(new MyClaimTaskCmd(claimTaskVO.getTaskId(), null));
        //taskService.claim(claimTaskVo.getTaskId(), null);
        log.info("流程ID:{},任务ID:{},已反签收!", currTask.getProcessInstanceId(), currTask.getId());
    }


    /**
     * 签收任务
     * 签收：一般情况就是多个候选人，或者候选组的情况下，要先把这个任务签收下来，以免别人又做了同样的任务。
     * https://blog.csdn.net/liuwenjun05101/article/details/103438645?spm=1001.2014.3001.5501
     *
     * @param claimTaskVO 参数
     * @return
     */
    @Override
    public void claimTask(ClaimTaskVO claimTaskVO) {
        TaskEntityImpl currTask = (TaskEntityImpl) taskService.createTaskQuery().taskId(claimTaskVO.getTaskId()).singleResult();
        assert currTask != null;

        FlowCommentInfo commentInfo = new FlowCommentInfo(CommentTypeEnum.FQS.toString(), claimTaskVO.getUserCode(),
                claimTaskVO.getProcessInstanceId(),
                claimTaskVO.getMessage());
        this.addFlowCommentInfo(commentInfo);
        //2.签收
        managementService.executeCommand(new MyClaimTaskCmd(claimTaskVO.getTaskId(), claimTaskVO.getUserCode()));
        //taskService.claim(claimTaskVO.getTaskId(), claimTaskVO.getUserCode());
        log.info("流程ID:{},任务ID:{},已签收!", currTask.getProcessInstanceId(), currTask.getId());
    }

    /**
     * 委派任务
     * 解释：委派
     * A由于某些原因不能处理该任务，可以把任务委派给用户B代理，当B处理完成之后再次回到用户A这里，在这个过程中A是任务的所有者，B是该任务的办理人
     * A->B->A
     * https://blog.csdn.net/liuwenjun05101/article/details/103430327?spm=1001.2014.3001.5501
     *
     * @param delegateTaskVO 参数
     * @return
     */
    @Override
    public void delegateTask(DelegateTaskVO delegateTaskVO) {
        TaskEntityImpl currTask = (TaskEntityImpl) taskService.createTaskQuery().taskId(delegateTaskVO.getTaskId()).singleResult();
        assert currTask != null;
        //1.添加审批意见
        FlowCommentInfo commentInfo = new FlowCommentInfo(CommentTypeEnum.WP.toString(), delegateTaskVO.getUserCode(),
                delegateTaskVO.getProcessInstanceId(),
                delegateTaskVO.getMessage());
        this.addFlowCommentInfo(commentInfo);
        //2.设置审批人就是当前登录人
        taskService.setAssignee(delegateTaskVO.getTaskId(), delegateTaskVO.getUserCode());
        //3.执行委派
        taskService.delegateTask(delegateTaskVO.getTaskId(), delegateTaskVO.getDelegateUserCode());
        log.info("流程ID:{},任务ID:{},已委派!", currTask.getProcessInstanceId(), currTask.getId());
    }

    /**
     * 转办
     * https://blog.csdn.net/liuwenjun05101/article/details/103420401?spm=1001.2014.3001.5501
     *
     * @param turnTaskVO 转办任务VO
     * @return 返回信息
     */
    @Override
    public void turnTask(TurnTaskVO turnTaskVO) {
        TaskEntityImpl currTask = (TaskEntityImpl) taskService.createTaskQuery().taskId(turnTaskVO.getTaskId()).singleResult();
        assert currTask != null;

        //1.生成历史记录
        TaskEntity task = this.createSubTask(currTask, turnTaskVO.getUserCode());
        //2.添加审批意见
        FlowCommentInfo commentInfo = new FlowCommentInfo(CommentTypeEnum.ZB.toString(), turnTaskVO.getUserCode(),
                task.getId(),
                turnTaskVO.getProcessInstanceId(),
                turnTaskVO.getMessage());
        this.addFlowCommentInfo(commentInfo);
        taskService.complete(task.getId());
        //3.转办
        taskService.setAssignee(turnTaskVO.getTaskId(), turnTaskVO.getTurnToUserId());
        taskService.setOwner(turnTaskVO.getTaskId(), turnTaskVO.getUserCode());
        log.info("流程ID:{},任务ID:{},已转办!", currTask.getProcessInstanceId(), currTask.getId());
    }

    /**
     * 通过任务id判断当前节点是不是并行网关的节点
     *
     * @param taskId 任务id
     * @return
     */
    @Override
    public boolean checkParallelGatewayNode(String taskId) {
        boolean flag = false;
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        String executionId = task.getExecutionId();
        Execution execution = runtimeService.createExecutionQuery().executionId(executionId).singleResult();
        String pExecutionId = execution.getParentId();
        Execution pExecution = runtimeService.createExecutionQuery().executionId(pExecutionId).singleResult();
        if (pExecution != null) {
            String ppExecutionId = pExecution.getParentId();
            long count = runtimeService.createExecutionQuery().executionId(ppExecutionId).count();
            if (count == 0) {
                flag = true;
            }
        }
        return flag;
    }

    /**
     * 通过流程实例id获取流程实例的待办任务审批人列表
     *
     * @param processInstanceId 流程实例id
     * @return
     */
    @Override
    public List<User> getApproverList(String processInstanceId) {
        List<User> userDataList = new ArrayList<>();
        List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        if (CollUtil.isNotEmpty(list)) {
            list.forEach(task -> {
                if (StrUtil.isNotBlank(task.getAssignee())) {
                    //1.审批人ASSIGNEE_是用户id
                    User user = identityService.createUserQuery().userId(task.getAssignee()).singleResult();
                    if (user != null) {
                        userDataList.add(user);
                    }
                    //2.审批人ASSIGNEE_是组id
                    List<User> gusers = identityService.createUserQuery().memberOfGroup(task.getAssignee()).list();
                    if (CollUtil.isNotEmpty(gusers)) {
                        userDataList.addAll(gusers);
                    }
                } else {
                    List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(task.getId());
                    if (CollUtil.isNotEmpty(identityLinks)) {
                        identityLinks.forEach(identityLink -> {
                            //3.审批人ASSIGNEE_为空,用户id
                            if (StrUtil.isNotBlank(identityLink.getUserId())) {
                                User user = identityService.createUserQuery().userId(identityLink.getUserId()).singleResult();
                                if (user != null) {
                                    userDataList.add(user);
                                }
                            } else {
                                //4.审批人ASSIGNEE_为空,组id
                                List<User> gusers = identityService.createUserQuery().memberOfGroup(identityLink.getGroupId()).list();
                                if (CollUtil.isNotEmpty(gusers)) {
                                    userDataList.addAll(gusers);
                                }
                            }
                        });
                    }
                }
            });
        }
        return userDataList;
    }
}
